/*
 * graph2D
 * Copyright (c) 2009 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#if !defined(___GRAPH2D_VECTOR2_H___)
#define ___GRAPH2D_VECTOR2_H___

#include <mana_stream.h>
#include <math.h>

namespace Graph2D
{
	typedef struct Vector2
	{
		float x, y;

		Vector2()
		{
			zero();
		}

		Vector2(const float x, const float y)
		{
			set(x, y);
		}

		Vector2(const Vector2& position)
		{
			set(position);
		}

		void serialize(mana_stream* stream) const
		{
			mana_stream_push_float(stream, x);
			mana_stream_push_float(stream, y);
		}

		void deserialize(mana_stream* stream)
		{
			x = mana_stream_pop_float(stream);
			y = mana_stream_pop_float(stream);
		}

		void set(const float x, const float y)
		{
			this->x = x;
			this->y = y;
		}

		void set(const Vector2& position)
		{
			this->x = position.x;
			this->y = position.y;
		}

		void zero()
		{
			set(0.f, 0.f);
		}

		bool empty() const
		{
			return (x == 0.f || y == 0.f);
		}

		void negate()
		{
			set(-x, -y);
		}

		void normalize()
		{
			const float l = length();
			if(l > 0.f)
			{
				*this *= (1.f / l);
			}
		}

		Vector2 floor()
		{
			Vector2 vector;
			vector.x = floorf(x);
			vector.y = floorf(y);
			return vector;
		}
		/*
		Vector2 round()
		{
			Vector2 vector;
			vector.x = roundf(x);
			vector.y = roundf(y);
			return vector;
		}
		*/
		float length() const
		{
			return sqrt(squareLength());
		}

		float squareLength() const
		{
			return x*x + y*y;
		}

		float distance(const Vector2& position) const
		{
			return sqrt(squareDistance(position));
		}

		float squareDistance(const Vector2& position) const
		{
			const float dx = x - position.x;
			const float dy = y - position.y;
			return dx*dx + dy*dy;
		}

		float dot(const Vector2& position) const
		{
			return x * position.x + y * position.y;
		}

		float cross(const Vector2& position) const
		{
			return x * position.y - y * position.x;
		}

		//void calcLerp(const Vector2& start, const Vector2& end, const float value);
		//void calcLerpAngles(const Vector2& start, const Vector2& end, const float value);

		Vector2 operator-() const
		{
			return Vector2(-x, -y);
		}

		Vector2& operator=(const Vector2& position)
		{
			set(position);
			return *this;
		}

		Vector2 operator+(const Vector2& position) const
		{
			return Vector2(x + position.x, y + position.y);
		}

		Vector2 operator-(const Vector2& position) const
		{
			return Vector2(x - position.x, y - position.y);
		}

		Vector2 operator*(const Vector2& position) const
		{
			return Vector2(x * position.x, y * position.y);
		}

		Vector2 operator/(const Vector2& position) const
		{
			return Vector2(x / position.x, y / position.y);
		}

		Vector2 operator+(const float scalar) const
		{
			return Vector2(x + scalar, y + scalar);
		}

		Vector2 operator-(const float scalar) const
		{
			return Vector2(x - scalar, y - scalar);
		}

		Vector2 operator*(const float scalar) const
		{
			return Vector2(x * scalar, y * scalar);
		}

		Vector2 operator/(const float scalar) const
		{
			return Vector2(x / scalar, y / scalar);
		}

		Vector2& operator+=(const Vector2& position)
		{
			x += position.x;
			y += position.y;
			return *this;
		}

		Vector2& operator-=(const Vector2& position)
		{
			x -= position.x;
			y -= position.y;
			return *this;
		}

		Vector2& operator*=(const Vector2& position)
		{
			x *= position.x;
			y *= position.y;
			return *this;
		}

		Vector2& operator/=(const Vector2& position)
		{
			x /= position.x;
			y /= position.y;
			return *this;
		}

		Vector2& operator+=(const float scalar)
		{
			x += scalar;
			y += scalar;
			return *this;
		}

		Vector2& operator-=(const float scalar)
		{
			x -= scalar;
			y -= scalar;
			return *this;
		}

		Vector2& operator*=(const float scalar)
		{
			x *= scalar;
			y *= scalar;
			return *this;
		}

		Vector2& operator/=(const float scalar)
		{
			x /= scalar;
			y /= scalar;
			return *this;
		}

		bool operator==(const Vector2& position) const
		{
			return (x == position.x && y == position.y);
		}

		bool operator!=(const Vector2& position) const
		{
			return (x != position.x || y != position.y);
		}

		float operator[](const int index)
		{
			return static_cast<float*>(&x)[index];
		}

		float operator[](const int index) const
		{
			return static_cast<const float*>(&x)[index];
		}

		operator float*()
		{
			return &x;
		}

		operator const float*() const
		{
			return &x;
		}
	}Vector2;
}

#endif
